//
// Created by xie on 2021/1/12.
//

#include "Operation.h"
Operation::Operation(string str, map<string, int64_t> &data) : base_data(data) {
        this->meta = str;
        this->analyse(str);
}

int64_t Operation::getResult(){
    if (type == "base" || type == "root")
        return data[0];
    else if (type == "delta")
        return data[2];
    return data[1];
}

void Operation::update(){
    //before updating data, call update of sons
    for (auto & i : vec_op)
        i.update();

    //then we calculate values out of sub ops
    if (vec_op.size() != 0) {
        vector<int64_t> numbers;
        for (auto &i : vec_op)
            numbers.push_back(i.getResult());
        deque<int64_t> que;
        que.push_back(numbers[0]);
        for (int i = 1; i < numbers.size(); i++) {
            if (calculator[i - 1] == '+')
                que.push_back(numbers[i]);
            else if (calculator[i - 1] == '-')
                que.push_back(-numbers[i]);
            else {
                int64_t n1 = que.back();
                que.pop_back();
                int64_t n2 = numbers[i];
                if (calculator[i - 1] == '*')
                    que.push_back(n1 * n2);
                else que.push_back(n1 / n2);
            }
        }
        int64_t n = 0;
        for (auto &i : que)
            n += i;
        if (data.size() == 0)
            data.push_back(n);
        else data[0] = n;
    }
    else{
        if (data.size() == 0)
            data.push_back(base_data[meta]);
        else data[0] = base_data[meta];
    }
    //when it comes to here, result is done into data[0]
    if (type == "root");
    else if (type == "base"){
        if (data.size() == 0)
            data.push_back(base_data[meta]);
        else data[0] = base_data[meta];
    }
    else if (type == "min"){
        if (data.size() == 1)
            data.push_back(INT64_MAX);
        data[1] = min(data[1], data[0]);
    }
    else if (type == "max") {
        if (data.size() == 1)
            data.push_back(INT64_MIN);
        data[1] = max(data[0], data[1]);
    }
    else if (type == "sum") {
        if (data.size() == 1)
            data.push_back(0);
        data[1] += data[0];
    }
    else if (type == "delta"){
        if (data.size() == 1){
            data.push_back(data[0]);
            data.push_back(0);
        }
        data[2] = data[0] - data[1];
        data[1] = data[0];
    }
}

void Operation::analyse(string str){

    //split with =+-*/
    if (str.length() == 0) return ;
    int posBegin = 0;
    int posEnd = 0;
    vector<string> depart;
    bool in_kuohao = false;
    while(posEnd != str.length()){
        if (str[posEnd] == '(') in_kuohao = true;
        else if (str[posEnd] == ')') in_kuohao = false;
        if (!in_kuohao && (str[posEnd] == '+' || str[posEnd] == '-' || str[posEnd] == '*'
                           || str[posEnd] == '/' || str[posEnd] == '=')) {
            if (str[posEnd] != '=')
                depart.push_back(str.substr(posBegin, posEnd - posBegin));
            posBegin = posEnd + 1;
            if (str[posEnd] != '=') calculator.push_back(str[posEnd]);

        }
        posEnd ++;
    }
    if (posBegin != posEnd &&
        (depart.size()!=0 || (depart.size()==0 && !only_alpha(str.substr(posBegin, posEnd - posBegin)))))
        depart.push_back(str.substr(posBegin, posEnd - posBegin));


    for (int i=0; i<depart.size(); i++){
        if (depart[i].find("sum") == 0){
            Operation tmp_op(depart[i].substr(4, depart[i].length()-5),base_data);
            tmp_op.type = "sum";
            vec_op.push_back(tmp_op);
        }
        else if (depart[i].find("max") == 0){
            Operation tmp_op(depart[i].substr(4, depart[i].length()-5),base_data);
            tmp_op.type = "max";
            vec_op.push_back(tmp_op);
        }
        else if (depart[i].find("min") == 0){
            Operation tmp_op(depart[i].substr(4, depart[i].length()-5),base_data);
            tmp_op.type = "min";
            vec_op.push_back(tmp_op);
        }
        else if (depart[i].find("delta") == 0){
            Operation tmp_op(depart[i].substr(6, depart[i].length()-7),base_data);
            tmp_op.type = "delta";
            vec_op.push_back(tmp_op);
        }
        else{
            Operation tmp_op(depart[i],base_data);
            tmp_op.type = "base";
            vec_op.push_back(tmp_op);
        }
    }
}
